package com.rickpan.service;

import com.rickpan.entity.FileInfo;
import com.rickpan.exception.BusinessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

/**
 * 文件存储服务
 * 负责文件的物理存储、哈希计算、路径管理等
 */
@Service
public class FileStorageService {
    
    private static final Logger logger = LoggerFactory.getLogger(FileStorageService.class);
    
    @Value("${app.file.storage.path:./uploads}")
    private String storagePath;
    
    @Value("${app.file.storage.max-size:104857600}") // 100MB
    private long maxFileSize;
    
    // 分片上传临时目录
    private static final String CHUNK_TEMP_DIR = "chunks";
    
    // 缩略图目录
    private static final String THUMBNAIL_DIR = "thumbnails";
    
    /**
     * 初始化存储目录
     */
    public void initStorageDirectories() {
        try {
            // 创建主存储目录
            Path mainPath = Paths.get(storagePath);
            Files.createDirectories(mainPath);
            
            // 创建分片临时目录
            Path chunkPath = mainPath.resolve(CHUNK_TEMP_DIR);
            Files.createDirectories(chunkPath);
            
            // 创建缩略图目录
            Path thumbnailPath = mainPath.resolve(THUMBNAIL_DIR);
            Files.createDirectories(thumbnailPath);
            
            logger.info("存储目录初始化完成: {}", mainPath.toAbsolutePath());
            
        } catch (IOException e) {
            logger.error("存储目录初始化失败", e);
            throw new BusinessException("存储目录初始化失败");
        }
    }
    
    /**
     * 生成文件存储路径
     * 按日期分目录存储: /uploads/2024/01/15/uuid.ext
     */
    public String generateStoragePath(String originalFileName) {
        LocalDate now = LocalDate.now();
        String dateDir = now.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        
        // 生成唯一文件名
        String extension = getFileExtension(originalFileName);
        String fileName = UUID.randomUUID().toString() + extension;
        
        return dateDir + "/" + fileName;
    }
    
    /**
     * 获取文件扩展名
     */
    public String getFileExtension(String fileName) {
        if (fileName == null || !fileName.contains(".")) {
            return "";
        }
        return fileName.substring(fileName.lastIndexOf("."));
    }
    
    /**
     * 获取文件的绝对路径
     */
    public Path getAbsolutePath(String relativePath) {
        return Paths.get(storagePath).resolve(relativePath);
    }
    
    /**
     * 存储单个文件
     */
    public String storeFile(MultipartFile file) throws IOException {
        if (file.isEmpty()) {
            throw new BusinessException("文件不能为空");
        }
        
        if (file.getSize() > maxFileSize) {
            throw new BusinessException("文件大小超过限制");
        }
        
        String relativePath = generateStoragePath(file.getOriginalFilename());
        Path targetPath = getAbsolutePath(relativePath);
        
        // 确保目录存在
        Files.createDirectories(targetPath.getParent());
        
        // 保存文件
        try (InputStream inputStream = file.getInputStream()) {
            Files.copy(inputStream, targetPath, StandardCopyOption.REPLACE_EXISTING);
        }
        
        logger.info("文件存储成功: {} -> {}", file.getOriginalFilename(), relativePath);
        return relativePath;
    }
    
    /**
     * 存储分片文件
     */
    public String storeChunk(String uploadId, int chunkIndex, MultipartFile chunk) throws IOException {
        if (chunk.isEmpty()) {
            throw new BusinessException("分片文件不能为空");
        }
        
        // 分片存储路径: chunks/uploadId/chunk_index
        String chunkDir = CHUNK_TEMP_DIR + "/" + uploadId;
        String chunkFileName = "chunk_" + chunkIndex;
        String relativePath = chunkDir + "/" + chunkFileName;
        
        Path targetPath = getAbsolutePath(relativePath);
        
        // 确保目录存在
        Files.createDirectories(targetPath.getParent());
        
        // 保存分片
        try (InputStream inputStream = chunk.getInputStream()) {
            Files.copy(inputStream, targetPath, StandardCopyOption.REPLACE_EXISTING);
        }
        
        logger.debug("分片存储成功: uploadId={}, chunkIndex={}, size={}", 
                    uploadId, chunkIndex, chunk.getSize());
        return relativePath;
    }
    
    /**
     * 合并分片文件
     */
    public String mergeChunks(String uploadId, String originalFileName, int totalChunks) throws IOException {
        // 生成最终文件路径
        String finalPath = generateStoragePath(originalFileName);
        Path targetPath = getAbsolutePath(finalPath);
        
        // 确保目录存在
        Files.createDirectories(targetPath.getParent());
        
        // 分片目录
        Path chunkDir = getAbsolutePath(CHUNK_TEMP_DIR + "/" + uploadId);
        
        try (FileOutputStream fos = new FileOutputStream(targetPath.toFile());
             BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            
            // 按顺序合并分片
            for (int i = 0; i < totalChunks; i++) {
                Path chunkPath = chunkDir.resolve("chunk_" + i);
                
                if (!Files.exists(chunkPath)) {
                    throw new BusinessException("分片文件缺失: chunk_" + i);
                }
                
                try (FileInputStream fis = new FileInputStream(chunkPath.toFile());
                     BufferedInputStream bis = new BufferedInputStream(fis)) {
                    
                    byte[] buffer = new byte[8192];
                    int bytesRead;
                    while ((bytesRead = bis.read(buffer)) != -1) {
                        bos.write(buffer, 0, bytesRead);
                    }
                }
            }
        }
        
        // 清理分片文件
        cleanupChunks(uploadId);
        
        logger.info("分片合并完成: uploadId={}, finalPath={}, totalChunks={}", 
                   uploadId, finalPath, totalChunks);
        return finalPath;
    }
    
    /**
     * 清理分片文件
     */
    public void cleanupChunks(String uploadId) {
        try {
            Path chunkDir = getAbsolutePath(CHUNK_TEMP_DIR + "/" + uploadId);
            if (Files.exists(chunkDir)) {
                // 删除目录及其所有内容
                Files.walk(chunkDir)
                     .sorted((a, b) -> b.compareTo(a)) // 先删除文件，再删除目录
                     .forEach(path -> {
                         try {
                             Files.delete(path);
                         } catch (IOException e) {
                             logger.warn("删除分片文件失败: {}", path, e);
                         }
                     });
                logger.debug("分片文件清理完成: uploadId={}", uploadId);
            }
        } catch (IOException e) {
            logger.warn("清理分片文件失败: uploadId={}", uploadId, e);
        }
    }
    
    /**
     * 删除文件
     */
    public boolean deleteFile(String relativePath) {
        try {
            Path filePath = getAbsolutePath(relativePath);
            if (Files.exists(filePath)) {
                Files.delete(filePath);
                logger.info("文件删除成功: {}", relativePath);
                return true;
            }
            return false;
        } catch (IOException e) {
            logger.error("文件删除失败: {}", relativePath, e);
            return false;
        }
    }
    
    /**
     * 检查文件是否存在
     */
    public boolean fileExists(String relativePath) {
        Path filePath = getAbsolutePath(relativePath);
        return Files.exists(filePath);
    }
    
    /**
     * 获取文件大小
     */
    public long getFileSize(String relativePath) throws IOException {
        Path filePath = getAbsolutePath(relativePath);
        return Files.size(filePath);
    }
    
    /**
     * 计算文件SHA256哈希
     */
    public String calculateFileSHA256(String relativePath) throws IOException {
        Path filePath = getAbsolutePath(relativePath);
        return calculateFileSHA256(filePath);
    }
    
    /**
     * 计算文件SHA256哈希
     */
    public String calculateFileSHA256(Path filePath) throws IOException {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            
            try (InputStream fis = Files.newInputStream(filePath);
                 BufferedInputStream bis = new BufferedInputStream(fis)) {
                
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = bis.read(buffer)) != -1) {
                    digest.update(buffer, 0, bytesRead);
                }
            }
            
            byte[] hashBytes = digest.digest();
            StringBuilder sb = new StringBuilder();
            for (byte b : hashBytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
            
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SHA-256算法不可用", e);
        }
    }
    
    /**
     * 计算分片MD5哈希
     */
    public String calculateChunkMD5(MultipartFile chunk) throws IOException {
        try (InputStream inputStream = chunk.getInputStream()) {
            return DigestUtils.md5DigestAsHex(inputStream);
        }
    }
    
    /**
     * 复制文件
     */
    public String copyFile(String sourceRelativePath, String targetFileName) throws IOException {
        Path sourcePath = getAbsolutePath(sourceRelativePath);
        String targetRelativePath = generateStoragePath(targetFileName);
        Path targetPath = getAbsolutePath(targetRelativePath);
        
        // 确保目录存在
        Files.createDirectories(targetPath.getParent());
        
        // 复制文件
        Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
        
        logger.info("文件复制成功: {} -> {}", sourceRelativePath, targetRelativePath);
        return targetRelativePath;
    }
    
    /**
     * 移动文件
     */
    public String moveFile(String sourceRelativePath, String targetFileName) throws IOException {
        Path sourcePath = getAbsolutePath(sourceRelativePath);
        String targetRelativePath = generateStoragePath(targetFileName);
        Path targetPath = getAbsolutePath(targetRelativePath);
        
        // 确保目录存在
        Files.createDirectories(targetPath.getParent());
        
        // 移动文件
        Files.move(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
        
        logger.info("文件移动成功: {} -> {}", sourceRelativePath, targetRelativePath);
        return targetRelativePath;
    }
    
    /**
     * 获取存储根路径
     */
    public String getStoragePath() {
        return storagePath;
    }
    
    /**
     * 获取最大文件大小限制
     */
    public long getMaxFileSize() {
        return maxFileSize;
    }

    /**
     * 复制文件
     * @param sourceStoragePath 源文件存储路径
     * @return 新文件的存储路径
     */
    public String copyFile(String sourceStoragePath) throws IOException {
        logger.info("复制文件 - 源路径: {}", sourceStoragePath);

        // 构建源文件完整路径
        Path sourcePath = Paths.get(storagePath, sourceStoragePath);

        if (!Files.exists(sourcePath)) {
            throw new BusinessException("源文件不存在: " + sourceStoragePath);
        }

        // 生成新的存储路径
        String newStoragePath = generateStoragePath(getFileExtension(sourceStoragePath));
        Path targetPath = Paths.get(storagePath, newStoragePath);

        try {
            // 确保目标目录存在
            Files.createDirectories(targetPath.getParent());

            // 复制文件
            Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);

            logger.info("文件复制成功 - 源: {}, 目标: {}", sourceStoragePath, newStoragePath);

            return newStoragePath;
        } catch (IOException e) {
            logger.error("文件复制失败", e);
            throw new IOException("文件复制失败: " + e.getMessage(), e);
        }
    }


}
